package csbuf

import (
	"encoding/binary"
	"math"
	"fmt"
	// "container/list"
)

var _ = fmt.Print

const (
	buftype_unknow		byte		= 0;
	buftype_bool		byte		= 1;
	buftype_uint8		byte		= 1;
	buftype_uint16		byte		= 2;
	buftype_uint32		byte		= 4;
	buftype_uint64		byte		= 8;
	buftype_int8		byte		= 1;
	buftype_int16		byte		= 2;
	buftype_int32		byte		= 4;
	buftype_int64		byte		= 8;
	buftype_float		byte		= 4;
	buftype_double		byte		= 8;
	buftype_string_l	byte		= 253;	// 长字符串
	buftype_string_s	byte		= 254;
	buftype_string		byte		= 255;
)

var UseAutoInt32 bool = true

type CsbufParser struct {
	data 	[]byte
	cursor	uint32
	posStack	[]uint32
}

func (p *CsbufParser) SetData(data []byte) {
	if p.data == nil || cap(p.data) < len(data) {
		if len(data) > 1024 {
			p.data = make([]byte, 0, len(data))
		} else {
			p.data = make([]byte, 0, 1024)
		}
	}
	p.data = p.data[:len(data)]
	copy(p.data, data)
	p.cursor = 0
}

func (p *CsbufParser) GetData() []byte {
	return p.data
}

func (p *CsbufParser) Clear() {
	if p.data == nil {
		p.data = make([]byte, 0, 1024)
	} else {
		p.data = p.data[:0]
	}
	p.cursor = 0
	if nil != p.posStack {
		p.posStack = p.posStack[:0]
	}
}

func (p *CsbufParser) SetBool(d bool, pos uint8) {
	 var b uint8 = 0
	 if d {
	 	b = 1
	 } else if 0 == pos {
	 	return
	 }
	p.data = append(p.data, byte(pos), buftype_bool, b)
}

func (p *CsbufParser) SetUint8(d uint8, pos uint8) {
	if d == 0 && 0 != pos {
		return
	}

	p.data = append(p.data, byte(pos), buftype_uint8, byte(d))
}

func (p *CsbufParser) SetUint16(d uint16, pos uint8) {
	if d == 0 && 0 != pos {
		return
	}

	p.data = append(p.data, byte(pos), buftype_uint16, 
		byte(d >> 8),
		byte(d))
}

func (p *CsbufParser) SetUint32(d uint32, pos uint8) {
	if d == 0 && 0 != pos {
		return
	}


	if UseAutoInt32 && d <= 255 {
		p.SetUint8(uint8(d), pos)
	} else if UseAutoInt32 && d <= 65535 {
		p.SetUint16(uint16(d), pos)
	} else {
		p.data = append(p.data, byte(pos), buftype_uint32, 
			byte(d >> 24),
			byte(d >> 16),
			byte(d >> 8),
			byte(d))
	}
}

func (p *CsbufParser) SetUint64(d uint64, pos uint8) {
	if d == 0 && 0 != pos {
		return
	}

	p.data = append(p.data, byte(pos), buftype_uint64, 
		byte(d >> 56),
		byte(d >> 48),
		byte(d >> 40),
		byte(d >> 32),
		byte(d >> 24),
		byte(d >> 16),
		byte(d >> 8),
		byte(d))
}

func (p *CsbufParser) SetInt8(d int8, pos uint8) {
	if d == 0 && 0 != pos {
		return
	}

	p.data = append(p.data, byte(pos), buftype_int8, byte(d))
}

func (p *CsbufParser) SetInt16(d int16, pos uint8) {
	if d == 0 && 0 != pos {
		return
	}

	p.data = append(p.data, byte(pos), buftype_int16, 
		byte(d >> 8),
		byte(d))
}

func (p *CsbufParser) SetInt32(d int32, pos uint8) {
	if d == 0 && 0 != pos {
		return
	}

	if UseAutoInt32 && d >= -128 && d <= 127 {
		p.SetInt8(int8(d), pos)
	} else if UseAutoInt32 && d >= -32768 && d <= 32767 {
		p.SetInt16(int16(d), pos)
	} else {
		p.data = append(p.data, byte(pos), buftype_int32, 
			byte(d >> 24),
			byte(d >> 16),
			byte(d >> 8),
			byte(d))
	}
}

func (p *CsbufParser) SetInt64(d int64, pos uint8) {
	if d == 0 && 0 != pos {
		return
	}

	p.data = append(p.data, byte(pos), buftype_int64, 
		byte(d >> 56),
		byte(d >> 48),
		byte(d >> 40),
		byte(d >> 32),
		byte(d >> 24),
		byte(d >> 16),
		byte(d >> 8),
		byte(d))
}

func (p *CsbufParser) SetFloat(d float32, pos uint8) {
	if d > -0.00000001 && d < 0.00000001 && 0 != pos {
		return
	}

	f := math.Float32bits(d)
	p.data = append(p.data, byte(pos), buftype_float,
		byte(f >> 24),
		byte(f >> 16),
		byte(f >> 8),
		byte(f))
}

func (p *CsbufParser) SetDouble(d float64, pos uint8) {
	if d > -0.00000001 && d < 0.00000001 && 0 != pos {
		return
	}

	f := math.Float64bits(d)
	p.data = append(p.data, byte(pos), buftype_double, 
		byte(f >> 56),
		byte(f >> 48),
		byte(f >> 40),
		byte(f >> 32),
		byte(f >> 24),
		byte(f >> 16),
		byte(f >> 8),
		byte(f))
}

func (p *CsbufParser) SetString(str string, pos uint8) {
	d := []byte(str)
	s := uint32(len(d))
	if s == 0 && 0 != pos {
		return
	}

	if s <= 255 {
		p.data = append(p.data, byte(pos), buftype_string_s,
			byte(uint8(s)))
	} else if s <= 65535 {
		p.data = append(p.data, byte(pos), buftype_string,
			byte(s >> 8),
			byte(s))
	} else {
		p.data = append(p.data, byte(pos), buftype_string_l,
			byte(s >> 24),
			byte(s >> 16),
			byte(s >> 8),
			byte(s))
	}
	p.data = append(p.data, d...)
}


func (p *CsbufParser) SetArray(d uint16, pos uint8) {
	if d == 0 && 0 != pos {
		return
	}

	if d <= 255 {
		p.SetUint8(uint8(d), pos)
	} else {
		p.SetUint16(d, pos)
	}
}

func (p *CsbufParser) SetMessage(pos uint8) {
	p.data = append(p.data, pos, buftype_string, byte(0), byte(0))
	p.posStack = append(p.posStack, uint32(len(p.data)))
}

func (p *CsbufParser) EndSetMessage() {
	pos := p.posStack[len(p.posStack) - 1]
	p.posStack = p.posStack[:len(p.posStack) - 1]

	l := uint32(len(p.data)) - pos
	if 0 == l {
		if 0 != p.data[len(p.data) - 4] {
			p.data = p.data[:len(p.data)-4]
		}
	} else if l <= 65535 {
		p.data[pos - 2] = byte(l >> 8)
		p.data[pos - 1] = byte(l)
	} else {
		p.data = append(p.data, byte(0), byte(0))
		for i := uint32(len(p.data)) - 3; i >= pos; i-- {
			p.data[i + 2] = p.data[i]
		}
		p.data[pos - 2] = byte(l >> 24)
		p.data[pos - 1] = byte(l >> 16)
		p.data[pos] = byte(l >> 8)
		p.data[pos + 1] = byte(l)
		p.data[pos - 3] = buftype_string_l
	}
}


func (p *CsbufParser) GetBool(pos uint8) bool {
	if b, ok := p.getByte(1, pos); ok {
		return uint8(b[0]) == 1
	}

	return false
}

func (p *CsbufParser) GetUint8(pos uint8) uint8 {
	if b, ok := p.getByte(1, pos); ok {
		return uint8(b[0])
	}

	return 0
}

func (p *CsbufParser) GetUint16(pos uint8) uint16 {
	if b, ok := p.getByte(2, pos); ok {
		return binary.BigEndian.Uint16(b)
	}

	return 0
}

func (p *CsbufParser) GetUint32(pos uint8) uint32 {
	// if b, ok := p.getByte(4, pos); ok {
	// 	return binary.BigEndian.Uint32(b)
	// }
	if b, n := p.getByteAtPos(pos); n > 0 {
		if 1 == n {
			return uint32(uint8(b[0]))
		} else if 2 == n {
			return uint32(binary.BigEndian.Uint16(b))
		} else if 4 == n {
			return binary.BigEndian.Uint32(b)
		}
	}

	return 0
}

func (p *CsbufParser) GetUint64(pos uint8) uint64 {
	if b, ok := p.getByte(8, pos); ok {
		return binary.BigEndian.Uint64(b)
	}

	return 0
}

func (p *CsbufParser) GetInt8(pos uint8) int8 {
	if b, ok := p.getByte(1, pos); ok {
		return int8(b[0])
	}

	return 0
}

func (p *CsbufParser) GetInt16(pos uint8) int16 {
	if b, ok := p.getByte(2, pos); ok {
		return int16(binary.BigEndian.Uint16(b))
	}

	return 0
}

func (p *CsbufParser) GetInt32(pos uint8) int32 {
	// if b, ok := p.getByte(4, pos); ok {
	// 	return int32(binary.BigEndian.Uint32(b))
	// }
	if b, n := p.getByteAtPos(pos); n > 0 {
		if 1 == n {
			return int32(int8(b[0]))
		} else if 2 == n {
			return int32(int16(binary.BigEndian.Uint16(b)))
		} else if 4 == n {
			return int32(binary.BigEndian.Uint32(b))
		}
	}

	return 0
}

func (p *CsbufParser) GetInt64(pos uint8) int64 {
	if b, ok := p.getByte(8, pos); ok {
		return int64(binary.BigEndian.Uint64(b))
	}

	return 0
}

func (p *CsbufParser) GetFloat(pos uint8) float32 {
	if b, ok := p.getByte(4, pos); ok {
		return math.Float32frombits(binary.BigEndian.Uint32(b))
	}

	return 0
}

func (p *CsbufParser) GetDouble(pos uint8) float64 {
	if b, ok := p.getByte(8, pos); ok {
		return math.Float64frombits(binary.BigEndian.Uint64(b))
	}

	return 0
}

func (p *CsbufParser) GetString(pos uint8) string {
	if p.setCursorWithPos(pos) {
		s := p.getSize()
		if p.cursor + 3 < s && p.data[p.cursor + 1] == buftype_string {
			var n uint32 = uint32(binary.BigEndian.Uint16(p.data[(p.cursor+2):]))
			var b []byte
			if p.cursor + 3 + n < s {
				b = p.data[(p.cursor + 4) : (p.cursor + 4 + n)]
			}
			p.cursor += 4 + n
			if b != nil {
				return string(b)
			}
		} else if p.cursor + 2 < s && p.data[p.cursor + 1] == buftype_string_s {
			var n uint32 = uint32(uint8(p.data[p.cursor+2]))
			var b []byte
			if p.cursor + 2 + n < s {
				b = p.data[(p.cursor + 3) : (p.cursor + 3 + n)]
			}
			p.cursor += 3 + n
			if b != nil {
				return string(b)
			}
		} else if p.cursor + 5 < s && p.data[p.cursor + 1] == buftype_string_l {
			var n uint32 = uint32(binary.BigEndian.Uint32(p.data[(p.cursor+2):]))
			var b []byte
			if p.cursor + 5 + n < s {
				b = p.data[(p.cursor + 6) : (p.cursor + 6 + n)]
			}
			p.cursor += 6 + n
			if b != nil {
				return string(b)
			}
		}
	}
	return ""
}

func (p *CsbufParser) GetArray(pos uint8) uint16 {
	if p.setCursorWithPos(pos) {
		s := p.getSize()
		if p.cursor + 1 < s {
			if p.data[p.cursor + 1] == uint8(1) {
				if p.cursor + 2 < s {
					p.cursor += 3
					return uint16(uint8(p.data[p.cursor-1]))
				}
			} else if p.data[p.cursor + 1] == uint8(2) {
				if p.cursor + 3 < s {
					p.cursor += 4
					return binary.BigEndian.Uint16(p.data[p.cursor - 2:])
				}
			}
		}
	}

	return 0
}

func (p *CsbufParser) GetMessage(pos uint8) uint32 {
	if p.setCursorWithPos(pos) {
		s := p.getSize()
		if p.cursor + 3 < s && p.data[p.cursor + 1] == buftype_string {
			ret := uint32(binary.BigEndian.Uint16(p.data[(p.cursor+2):]))
			p.cursor += 4
			p.posStack = append(p.posStack, p.cursor + ret)
			return ret
		} else if p.cursor + 5 < s && p.data[p.cursor + 1] == buftype_string_l {
			var ret uint32 = uint32(binary.BigEndian.Uint32(p.data[(p.cursor+2):]))
			p.cursor += 6
			p.posStack = append(p.posStack, p.cursor + ret)
			return ret
		} else {
			p.posStack = append(p.posStack, 0)
		}
	} else {
		p.posStack = append(p.posStack, p.cursor)
	}
	return 0
}

func (p *CsbufParser) EndGetMessage() {
	n := len(p.posStack)
	if n > 0 {
		p.cursor = p.posStack[n - 1]
		p.posStack = p.posStack[:n-1]
	}
}

func (p *CsbufParser) getByte(n uint32, pos uint8) ([]byte, bool) {
	if p.setCursorWithPos(pos) {
		s := p.getSize()
		if p.cursor + 1 + n < s {
			if p.data[p.cursor + 1] == uint8(n) {
				p.cursor += 2 + n
				return p.data[(p.cursor-n) : p.cursor], true
			}
		}
	}

	return nil, false
}

// 获取在 pos 上的数值，返回对应数据和位数
func (p *CsbufParser) getByteAtPos(pos uint8) ([]byte, uint32) {
	if p.setCursorWithPos(pos) {
		s := p.getSize()
		if p.cursor + 1 < s {
			n := uint32(p.data[p.cursor + 1])
			if p.cursor + 1 + n < s {
				p.cursor += 2 + n
				return p.data[(p.cursor-n) : p.cursor], n
			}
		}
	}

	return nil, 0
}

func (p *CsbufParser) setCursorWithPos(pos uint8) bool {
	s := p.getSize()
	if p.cursor < s && p.cursor + 1 < s {
		point := uint8(p.data[p.cursor])
		if point == pos {
			return true
		} else if point < pos {
			if buftype_string == p.data[p.cursor + 1] {
				p.cursor += 2
				if p.cursor + 2 >= s {
					return false
				}
				var l uint32 = uint32(binary.BigEndian.Uint16(p.data[p.cursor:]))
				p.cursor += 2 + l
			} else if buftype_string_s == p.data[p.cursor + 1] {
				p.cursor += 2
				if p.cursor + 1 >= s {
					return false
				}
				var l uint32 = uint32(uint8(p.data[p.cursor+2]))
				p.cursor += 1 + l
			} else if buftype_string_l == p.data[p.cursor + 1] {
				p.cursor += 2
				if p.cursor + 4 >= s {
					return false
				}
				var l uint32 = uint32(binary.BigEndian.Uint32(p.data[p.cursor:]))
				p.cursor += 4 + l
			} else {
				p.cursor += 2 + uint32(p.data[p.cursor + 1])
			}
			return p.setCursorWithPos(pos)
		}
	}
	return false
}

func (p *CsbufParser) getSize() uint32 {
	n := len(p.posStack)
	if n > 0 {
		return p.posStack[n - 1]
	}
	return uint32(len(p.data))
}
